import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Date;

public class Client
{
    private String nickName;
    private String serverIp;
    private int serverPort;
    private int clientPort;
    private Listen listener;

    public Client(String nickName, String serverIp, int serverPort, int clientPort)// constructor
                                                                                   // //
                                                                                   // constructor
    {
        this.nickName = nickName;
        this.serverIp = serverIp;
        this.serverPort = serverPort;
        this.clientPort = clientPort;
    }

    public void parse(String str)
    {
        int firstSpace = str.indexOf(' ');
        if (firstSpace == -1)
        {
            System.out.println("ERROR command mode");
            return;
        }

        String command = str.substring(0, firstSpace);

        if (command.equals("send"))
        {
            int secondSpace = str.indexOf(' ', firstSpace + 1);

            if (secondSpace == -1)
            {
                System.out.println("ERROR command mode");
            }
            else
            {
                String name = str.substring(firstSpace + 1, secondSpace);
                String message = str.substring(secondSpace + 1);

                String chatIp = "";
                int chatPort = 0;
                boolean chatStatus = false;
                for (TableServer entry : listener.table)// for each element
                                                        // "entry" in table
                {
                    if (entry.nickName.equals(name))
                    {
                        chatIp = entry.clientIp;
                        chatPort = entry.clientPort;
                        chatStatus = entry.isOnline;
                    }
                    else
                    {

                    }
                }

                if (chatStatus)
                {
                    Packet packet = new Packet();
                    packet.type = Packet.PacketType.Chatting;
                    Msg messageall = new Msg(nickName, name, message);

                    packet.data = messageall;
                    SendAndReceive.send(packet, chatIp, chatPort);
                    listener.isACKArrived = false;
                    try
                    {
                        Thread.sleep(500);
                    }
                    catch (InterruptedException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    if (listener.isACKArrived == false)
                    {
                        System.out.println(">>> [No ACK from " + name + ", message sent to server.]");
                        for (int i = 0; i < 5; i++)
                        {
                            SendAndReceive.send(packet, serverIp, serverPort);
                            try
                            {
                                Thread.sleep(500);
                            }
                            catch (InterruptedException e)
                            {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            if (listener.isACKArrived)
                            {
                                System.out.println(">>> [Messages received by the server and saved]");
                                break;
                            }
                            if (i == 4)
                            {
                                System.out.println(">>> [No response from server. Exit]");
                                System.exit(1);
                            }
                        }   
                    }
                    else
                    {
                        System.out.println(">>> [message received by " + name + ".]");
                    }

                }
                else
                {
                    Packet packet = new Packet();
                    packet.type = Packet.PacketType.Chatting;
                    Msg messageall = new Msg(nickName, name, message);

                    packet.data = messageall;
                    for (int i = 0; i < 5; i++)
                    {
                        SendAndReceive.send(packet, serverIp, serverPort);
                        try
                        {
                            Thread.sleep(500);
                        }
                        catch (InterruptedException e)
                        {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        if (listener.isACKArrived)
                        {
                            System.out.println(">>> [Messages received by the server and saved]");
                            break;
                        }
                        if (i == 4)
                        {
                            System.out.println(">>> [No response from server. Exit]");
                            System.exit(1);
                        }
                    }   
                }
                // TODO: send <name> <message>
            }
        }
        else if (command.equals("dereg"))
        {

            String nick_name = str.substring(firstSpace + 1);

            if (nick_name.equals(nickName))
            {
                Packet packetDe = new Packet();
                packetDe.type = Packet.PacketType.DeRegistrating;
                packetDe.data = nick_name;
                listener.isACKArrived = false;
                for (int i = 0; i < 5; i++)
                {
                    SendAndReceive.send(packetDe, serverIp, serverPort);
                    try
                    {
                        Thread.sleep(500);
                    }
                    catch (InterruptedException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    if (listener.isACKArrived)
                    {
                        System.out.println(">>> [You are Offline. Bye.]");
                        break;
                    }
                }

                if (!listener.isACKArrived)
                {
                    System.out.println(">>> [Server not responding]");
                    System.out.println(">>> [Exiting]");
                    System.exit(0);
                }
            }
            else
            {
                System.out.println(">>> Wrong Client");
            }
            // TODO: dereg <nick-name>
        }
        else if (command.equals("reg"))
        {
            String nick_name = str.substring(firstSpace + 1);
            if (!nick_name.equals(nickName))
            {
                System.out.println("Wrong Client");

            }
            else
            {
                register();
            }
        }
        else
        {
            System.out.println("ERROR command word");
        }

    }

    public void run()
    {
        listener = new Listen(clientPort);
        Thread listen = new Thread(listener);
        listen.start();

        register();

        while (true)
        {
            System.out.print(">>> ");
            InputStreamReader converter = new InputStreamReader(System.in);
            BufferedReader in = new BufferedReader(converter);
            String input = "";
            try
            {
                input = in.readLine();
                parse(input);
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

    private void register()
    {
        TableServer clientregistration = new TableServer(nickName, null, clientPort, true);
        Packet packet = new Packet();
        packet.type = Packet.PacketType.Registration;
        packet.data = clientregistration;
        SendAndReceive.send(packet, serverIp, serverPort);
        System.out.println(">>> [Welcome, You are registered.]");
    }

}

class Listen implements Runnable
{
    public boolean isACKArrived;
    private int receivePort;
    public ArrayList<TableServer> table;

    public Listen(int receivePort)// gou zao function constructor
    {
        this.receivePort = receivePort;
        table = new ArrayList<TableServer>();
    }

    @Override
    public void run()
    {
        SendAndReceive test = new SendAndReceive(receivePort);// listen
        while (true)
        {
            DatagramPacket receive = test.receive(); // receive
            Packet packet = (Packet) Serializer.byteArrayToObject(receive.getData());
            if (packet.type == Packet.PacketType.UpdateTable)
            {
                table = (ArrayList<TableServer>) packet.data;
                System.out.println(">>> [Client table updated.]");
            }

            if (packet.type == Packet.PacketType.Chatting)
            {
                Msg output = (Msg) packet.data;
                System.out.println(">>> " + output.senderNickName + ":" + output.msg);

                String a = "";
                int b = 0;
                for (TableServer entrydee : table)// for each element
                // "entry" in table
                {
                    if (entrydee.nickName.equals(output.senderNickName))
                    {
                        a = entrydee.clientIp;
                        b = entrydee.clientPort;
                    }
                    else
                    {

                    }
                }

                Packet packetACK = new Packet();
                packetACK.type = Packet.PacketType.ACK;
                packetACK.data = packet.data;
                SendAndReceive.send(packetACK, a, b);
                System.out.print(">>> ");

            }

            if (packet.type == Packet.PacketType.ACKdeg)
            {
                this.isACKArrived = true;
            }
            if (packet.type == Packet.PacketType.ACK)
            {
                this.isACKArrived = true;
            }
            if (packet.type == Packet.PacketType.OfflineMsgs)
            {
                System.out.println(">>> [You have messages]");
                for (TableOffline offmsg : (ArrayList<TableOffline>) packet.data)
                    System.out.println(">>>" + offmsg.sender_nickName + ":" + offmsg.time_stamp + " " + offmsg.msg);
            }

        }

    }

}
